Skip to content

fix: tighten JSON, JSONL, and SSE content type matching - #277

Merged
jbeckwith-oai merged 2 commits into
openai:mainfrom
fallintoplace:fix/jsonl-content-type-regex
Aug 10, 2026
Merged

fix: tighten JSON, JSONL, and SSE content type matching#277
jbeckwith-oai merged 2 commits into
openai:mainfrom
fallintoplace:fix/jsonl-content-type-regex

Conversation

@fallintoplace

@fallintoplace fallintoplace commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • require JSON and JSONL content types to match an exact, case-insensitive media type boundary
  • keep JSON and JSONL dispatch disjoint, including mixed-case application/jsonL
  • preserve supported x-ndjson, x-ldjson, jsonl, and x-jsonl media types with optional parameters and horizontal whitespace
  • apply the same case-insensitive boundary handling to Server-Sent Events
  • add matcher and end-to-end encode/decode regressions for valid, malformed, suffixed, and unrelated content types

Why

encode_content and decode_content select JSON, JSONL, and SSE behavior by content type. The previous JSONL regex matched unrelated values, while the JSON matcher could claim mixed-case JSONL before JSONL dispatch. Prefix-only matching also accepted invalid suffixes such as application/json-seq and text/event-streaming.

These cases could parse the wrong format, raise parser errors, or return the wrong decoded shape.

Fixes #276.

Test plan

  • mise exec ruby@4.0.6 -- ./scripts/test
  • mise exec ruby@4.0.6 -- bundle exec rake lint
  • mise exec ruby@4.0.6 -- bundle exec rake build:gem
  • GitHub Actions: 11 passed, 0 failed

@fallintoplace
fallintoplace marked this pull request as ready for review May 19, 2026 17:41
@fallintoplace
fallintoplace requested a review from a team as a code owner May 19, 2026 17:41
@jbeckwith-oai jbeckwith-oai added the generator Touches generated SDK files label Jul 16, 2026
@fallintoplace
fallintoplace force-pushed the fix/jsonl-content-type-regex branch from d89ace1 to 4f5db1e Compare July 16, 2026 23:29
@fallintoplace fallintoplace changed the title Tighten JSONL content type matching Fix false-positive JSONL content type matches Jul 16, 2026
@jbeckwith-oai
jbeckwith-oai force-pushed the fix/jsonl-content-type-regex branch from 4f5db1e to 5a7c815 Compare August 10, 2026 21:19
@jbeckwith-oai jbeckwith-oai changed the title Fix false-positive JSONL content type matches fix: tighten JSON, JSONL, and SSE content type matching Aug 10, 2026
@jbeckwith-oai
jbeckwith-oai force-pushed the fix/jsonl-content-type-regex branch from 5a7c815 to bf02d23 Compare August 10, 2026 21:34
@jbeckwith-oai
jbeckwith-oai added this pull request to the merge queue Aug 10, 2026
Merged via the queue into openai:main with commit 66359a0 Aug 10, 2026
12 checks passed
@openai-sdks openai-sdks Bot mentioned this pull request Aug 10, 2026
virelai2604 pushed a commit to virelai2604/openai-ruby that referenced this pull request Aug 15, 2026
Automated Release PR
---


##
[0.79.0](openai/openai-ruby@v0.78.0...v0.79.0)
(2026-08-14)


### Features

* add HTTP response observability
([openai#365](openai#365))
([48d36b7](openai@48d36b7))
* add Tapioca typing for structured outputs
([openai#364](openai#364))
([738e2b4](openai@738e2b4))
* **api:** Add new model identifiers and remove audit log source
([e0a4bc5](openai@e0a4bc5))
* **api:** add WebSocket stream IDs
([openai#387](openai#387))
([5880287](openai@5880287))
* **api:** add workload identity access token issued event
([openai#372](openai#372))
([9199779](openai@9199779))
* **api:** deprecate Sora video APIs
([openai#386](openai#386))
([0664456](openai@0664456))
* **client:** add default headers
([openai#369](openai#369))
([d516874](openai@d516874))
* expose request IDs
([openai#352](openai#352))
([c15fb7b](openai@c15fb7b))
* support Azure OpenAI v1
([openai#355](openai#355))
([c1d223e](openai@c1d223e))


### Bug Fixes

* **api:** Add new model identifiers and remove audit log source
([openai#360](openai#360))
([e0a4bc5](openai@e0a4bc5))
* **api:** allow nil timeout in client signatures
([openai#363](openai#363))
([e52ca61](openai@e52ca61))
* **api:** document file upload metadata defaults
([openai#361](openai#361))
([b4bc1ea](openai@b4bc1ea))
* coerce nested BaseModel fields
([openai#295](openai#295))
([e5152a9](openai@e5152a9))
* enable streaming when retrieving responses
([openai#413](openai#413))
([fd85182](openai@fd85182))
* encode multipart array and nested fields
([openai#348](openai#348))
([1c71a1e](openai@1c71a1e))
* honor workload identity environment defaults
([openai#398](openai#398))
([f4afea1](openai@f4afea1))
* keep required path parameters out of resource query strings
([openai#402](openai#402))
([6768a7c](openai@6768a7c))
* loading after ActiveSupport 6 subclass extensions
([openai#346](openai#346))
([20fbb09](openai@20fbb09))
* make SDK debug body logging fail closed
([openai#411](openai#411))
([c4f7284](openai@c4f7284))
* package every README-linked guide and example
([openai#382](openai#382))
([ddecd0f](openai@ddecd0f))
* preserve binary multipart stream reads
([openai#354](openai#354))
([b3a1ba7](openai@b3a1ba7))
* preserve existing BaseModel instances during coercion
([openai#400](openai#400))
([fbd8560](openai@fbd8560))
* preserve prefixed idempotency headers on redirects
([openai#404](openai#404))
([c380e13](openai@c380e13))
* preserve stream identity with HTTP logging
([openai#384](openai#384))
([640a1fe](openai@640a1fe))
* prevent symbol-keyed headers from bypassing security filters
([openai#383](openai#383))
([b8165af](openai@b8165af))
* redact sensitive query and form logging
([openai#389](openai#389))
([7aa54e4](openai@7aa54e4))
* reject invalid webhook signing secrets
([openai#403](openai#403))
([ad12458](openai@ad12458))
* remove unsound structured-output Tapioca compiler
([openai#405](openai#405))
([826769a](openai@826769a))
* restore RuboCop coverage for RBI files
([openai#388](openai#388))
([d2d4b5d](openai@d2d4b5d))
* return values from interruptible enumerator
([openai#350](openai#350))
([327dad7](openai@327dad7))
* strip credential headers on cross-origin redirects
([openai#391](openai#391))
([f50f08c](openai@f50f08c))
* tighten JSON, JSONL, and SSE content type matching
([openai#277](openai#277))
([66359a0](openai@66359a0))
* use API field names in structured output schemas
([openai#390](openai#390))
([d1e3cf3](openai@d1e3cf3))
* validate and bound retry delays
([openai#392](openai#392))
([fff94e7](openai@fff94e7))
* **webhooks:** support Rack and case-insensitive HTTP headers
([openai#401](openai#401))
([37de980](openai@37de980))


### Reverts

* nested BaseModel coercion
([openai#295](openai#295))
([openai#375](openai#375))
([65fa76e](openai@65fa76e))


### Chores

* Cover Ruby files at the SDK root
([openai#362](openai#362))
([d11f25b](openai@d11f25b))
* enforce boolean symbol lint
([openai#395](openai#395))
([fafdf2e](openai@fafdf2e))
* enforce deprecated constant lint
([openai#379](openai#379))
([0def912](openai@0def912))
* enforce duplicate match pattern lint
([openai#380](openai#380))
([7a8f2fb](openai@7a8f2fb))
* enforce empty else lint
([openai#396](openai#396))
([1e073c1](openai@1e073c1))
* enforce line length lint
([openai#406](openai#406))
([4592061](openai@4592061))
* enforce line length on base models
([openai#416](openai#416))
([aef1e33](openai@aef1e33))
* enforce line length on requires
([openai#415](openai#415))
([c24e045](openai@c24e045))
* enforce missing RuboCop enable directives
([openai#368](openai#368))
([50046bf](openai@50046bf))
* enforce missing super lint
([openai#393](openai#393))
([2a42bd0](openai@2a42bd0))
* enforce nonempty pattern branches
([openai#409](openai#409))
([9d22d5c](openai@9d22d5c))
* enforce redundant directive lint
([openai#366](openai#366))
([8448a4c](openai@8448a4c))
* enforce redundant exception lint
([openai#397](openai#397))
([cbda8bc](openai@cbda8bc))
* enforce symbol conversion lint
([openai#381](openai#381))
([0531498](openai@0531498))
* enforce useless assignment lint
([openai#394](openai#394))
([9688836](openai@9688836))
* guard RuboCop suppression directives
([openai#408](openai#408))
([5b6e0dc](openai@5b6e0dc))
* lint Ruby files repository-wide
([openai#377](openai#377))
([8ca8426](openai@8ca8426))
* remove Stainless attribution and infrastructure
([openai#371](openai#371))
([33d2c86](openai@33d2c86))
* require MFA for gem releases
([openai#378](openai#378))
([71798ec](openai@71798ec))


### Documentation

* **api:** describe response stream event unions
([openai#412](openai#412))
([87d18ef](openai@87d18ef))
* clarify file upload metadata
([openai#358](openai#358))
([698ac65](openai@698ac65)),
closes [openai#243](openai#243)
* document fiber scheduler concurrency
([openai#357](openai#357))
([25a7bbc](openai@25a7bbc))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

generator Touches generated SDK files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSONL content type matcher accepts unrelated strings

2 participants